﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;

namespace TimingFileCopy
{
    public class FileHelper
    {
        /// <summary>
        /// 获取临时文件名
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns></returns>
        public static string GetTempFileName(string fileName)
        {
            return Path.Combine(Path.GetTempPath(), string.IsNullOrEmpty(fileName) ? "~temp.tmp" : fileName);
        }

        /// <summary>
        /// 清除或创造路径
        /// </summary>
        /// <param name="dstPath"></param>
        public static void ClearOrCreatePath(string dstPath)
        {
            if (Directory.Exists(dstPath))
                ClearPath(dstPath);
            else
                CreateDirectoy(dstPath);
        }

        /// <summary>
        /// 清除路径
        /// </summary>
        /// <param name="dstPath"></param>
        public static void ClearPath(string dstPath)
        {
            try
            {
                if (!new System.IO.DirectoryInfo(dstPath).Exists)
                    return;

                foreach (string d in System.IO.Directory.GetFileSystemEntries(dstPath))
                {
                    if (System.IO.File.Exists(d))
                    {
                        System.IO.FileInfo fi = new System.IO.FileInfo(d);
                        DeleteFile(fi); // 直接删除其中的文件
                    }
                    else
                    {
                        System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(d);
                        DeletePath(di.FullName); // 删除子文件夹
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("清除文件夹失败-{0}", dstPath), ex);
            }
        }

        /// <summary>
        /// 删除目录下的所有文件（只删除文件）
        /// </summary>
        /// <param name="dstPath"></param>
        public static void DeleteFiles(string dstPath)
        {
            try
            {
                if (!new System.IO.DirectoryInfo(dstPath).Exists)
                    return;

                foreach (string d in System.IO.Directory.GetFiles(dstPath))
                {
                    if (System.IO.File.Exists(d))
                    {
                        System.IO.FileInfo fi = new System.IO.FileInfo(d);
                        DeleteFile(fi); // 直接删除其中的文件
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("删除目录下的所有文件失败-{0}", dstPath), ex);
            }
        }

        /// <summary>
        /// 删除路径下的所有文件
        /// </summary>
        /// <param name="dstPath"></param>
        public static void DeletePath(string dstPath)
        {
            try
            {
                if (!Directory.Exists(dstPath))
                    return;

                foreach (string d in System.IO.Directory.GetFileSystemEntries(dstPath))
                {
                    if (System.IO.File.Exists(d))
                    {
                        System.IO.FileInfo fi = new System.IO.FileInfo(d);
                        DeleteFile(fi); // 直接删除其中的文件
                    }
                    else
                        DeletePath(d); // 递归删除子文件夹
                }

                System.IO.Directory.Delete(dstPath); // 删除已空文件夹
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("删除文件夹失败-{0}", dstPath), ex);
            }
        }

        /// <summary>
        /// 复制文件
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dst"></param>
        public static void CopyFile(string src, string dst)
        {
            try
            {
                if (src.ToLower() == dst.ToLower())
                    return;

                if (!File.Exists(src))
                    return;

                FileHelper.DeleteFile(dst);
                FileHelper.CreateDirectoy(Path.GetDirectoryName(dst));

                System.IO.File.Copy(src, dst);
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("拷贝文件失败-{0}:{1}", src, dst), ex);
            }
        }

        /// <summary>
        /// 文件是否存在
        /// </summary>
        /// <param name="file">文件路径</param>
        /// <returns></returns>
        public static bool IsFileExist(string file)
        {
            return !string.IsNullOrEmpty(file) && File.Exists(file);
        }

        /// <summary>
        /// 是否存在该文件夹
        /// </summary>
        /// <param name="dir"></param>
        /// <returns></returns>
        public static bool IsDirExist(string dir)
        {
            return !string.IsNullOrEmpty(dir) && Directory.Exists(dir);
        }

        /// <summary>
        /// 移动文件夹
        /// </summary>
        /// <param name="src">源文件</param>
        /// <param name="dst">目标文件</param>
        public static void MoveFile(string src, string dst)
        {
            try
            {
                if (string.Equals(src, dst, StringComparison.CurrentCultureIgnoreCase))
                    return;

                if (!File.Exists(src))
                    return;

                FileHelper.DeleteFile(dst);
                FileHelper.CreateDirectoy(Path.GetDirectoryName(dst));

                System.IO.File.Move(src, dst);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"移动文件失败-{src}:{dst}", ex);
            }
        }

        /// <summary>
        /// 移动文件
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="dstPath"></param>
        public static void MovePath(string srcPath, string dstPath)
        {
            try
            {
                if (srcPath.ToLower() == dstPath.ToLower())
                    return;

                if (!Directory.Exists(srcPath))
                    return;

                ClearOrCreatePath(dstPath);

                System.IO.Directory.Move(srcPath, dstPath);
            }
            catch (Exception ex)
            {
                Console.WriteLine(string.Format("拷贝文件失败-{0}:{1}", srcPath, dstPath), ex);
            }
        }

        public static string DiskTypeInfo = "Disk";

        /// <summary>
        /// 通过拷贝删除方式移动
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="dstPath"></param>
        public static void MovePath_copy_delete(string srcPath, string dstPath)
        {
            try
            {
                if (srcPath.ToLower() == dstPath.ToLower())
                    return;

                if (!Directory.Exists(srcPath))
                    return;

                ClearOrCreatePath(dstPath);

                MoveDirectory(srcPath, dstPath);
                if (DiskTypeInfo == "Disk")
                {
                    DeletePath(srcPath);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"拷贝删除文件失败-{srcPath}:{dstPath}", ex);
            }
        }

        /// <summary>
        /// 移动整个文件夹
        /// </summary>
        /// <param name="srcDir"></param>
        /// <param name="tgtDir"></param>
        public static void MoveDirectory(string srcDir, string tgtDir)
        {
            if (string.Equals(srcDir, tgtDir, StringComparison.CurrentCultureIgnoreCase))
                return;

            if (!Directory.Exists(srcDir))
                return;

            DirectoryInfo source = new DirectoryInfo(srcDir);
            DirectoryInfo target = new DirectoryInfo(tgtDir);

            if (target.FullName.StartsWith(source.FullName, StringComparison.CurrentCultureIgnoreCase))
            {
                throw new Exception("父目录不能拷贝到子目录！");
            }

            if (!source.Exists)
            {
                return;
            }

            if (!target.Exists)
            {
                target.Create();
            }

            FileInfo[] files = source.GetFiles();

            foreach (var file in files)
            {
                if (DiskTypeInfo == "Disk")
                {
                    File.Move(file.FullName, target.FullName + @"\" + file.Name);
                }
                else
                    File.Copy(file.FullName, target.FullName + @"\" + file.Name);
            }

            DirectoryInfo[] dirs = source.GetDirectories();

            foreach (var dir in dirs)
            {
                MoveDirectory(dir.FullName, target.FullName + @"\" + dir.Name);
            }
        }

        /// <summary>
        /// 复制目录
        /// </summary>
        /// <param name="srcDir"></param>
        /// <param name="tgtDir"></param>
        public static void CopyDirectory(string srcDir, string tgtDir)
        {
            if (srcDir.ToLower() == tgtDir.ToLower())
                return;

            if (!Directory.Exists(srcDir))
                return;

            DirectoryInfo source = new DirectoryInfo(srcDir);
            DirectoryInfo target = new DirectoryInfo(tgtDir);

            if (target.FullName.StartsWith(source.FullName, StringComparison.CurrentCultureIgnoreCase))
            {
                throw new Exception("父目录不能拷贝到子目录！");
            }

            if (!source.Exists)
            {
                return;
            }

            if (!target.Exists)
            {
                target.Create();
            }

            FileInfo[] files = source.GetFiles();

            foreach (var file in files)
            {
                File.Copy(file.FullName, target.FullName + @"\" + file.Name, true);
            }

            DirectoryInfo[] dirs = source.GetDirectories();

            foreach (var dir in dirs)
            {
                CopyDirectory(dir.FullName, target.FullName + @"\" + dir.Name);
            }
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="fileName"></param>
        public static void DeleteFile(string fileName)
        {
            try
            {
                FileInfo fi = new System.IO.FileInfo(fileName);
                DeleteFile(fi);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"删除文件失败-{fileName}", ex);
            }
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="fi"></param>
        public static void DeleteFile(FileInfo fi)
        {
            try
            {
                if (fi.Exists)
                {
                    if (fi.Attributes.ToString().IndexOf("ReadOnly") != -1)
                        fi.Attributes = System.IO.FileAttributes.Normal;
                    System.IO.File.Delete(fi.FullName);

                    fi.Delete();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"删除文件失败-{fi.FullName}", ex);
            }
        }

        /// <summary>
        /// 从文件名创建Directoy
        /// </summary>
        /// <param name="fileName"></param>
        public static void CreateDirectoyFromFileName(string fileName)
        {
            try
            {
                string path = Path.GetDirectoryName(fileName);
                if (!new System.IO.DirectoryInfo(path).Exists)
                    System.IO.Directory.CreateDirectory(path);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"创建文件夹失败-{fileName}", ex);
            }
        }

        /// <summary>
        /// 由文件创建文件夹
        /// </summary>
        /// <param name="fileName"></param>
        public static void CreateDirectoyByFileName(string fileName)
        {
            try
            {
                string dir = Path.GetDirectoryName(fileName);
                CreateDirectoy(dir);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"由文件创建文件夹失败-{fileName}", ex);
            }
        }

        /// <summary>
        /// 创建文件夹
        /// </summary>
        /// <param name="dir"></param>
        public static void CreateDirectoy(string dir)
        {
            try
            {
                if (!new System.IO.DirectoryInfo(dir).Exists)
                    System.IO.Directory.CreateDirectory(dir);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"创建文件夹失败-{dir}", ex);
            }
        }

        /// <summary>
        /// 获取所有子目录
        /// </summary>
        /// <param name="root"></param>
        /// <param name="searchOption">指定是否搜索当前目录中，或当前目录和所有子目录。</param>
        /// <returns></returns>
        public static string[] GetAllSubDirs(string root, SearchOption searchOption)
        {
            string[] all = null;
            try
            {
                all = Directory.GetDirectories(root, "*", searchOption);
            }
            catch
            {
                // ignored
            }

            return all;
        }

        /// <summary>
        /// 获取所有子目录
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        public static List<string> GetAllPureSubDirs(string root)
        {
            List<string> list = new List<string>();
            try
            {
                string[] all = Directory.GetDirectories(root, "*", SearchOption.TopDirectoryOnly);
                foreach (string path in all)
                {
                    string s = System.IO.Path.GetFileName(path);
                    list.Add(s);
                }
            }
            catch
            {
                // ignored
            }

            return list;
        }

        /// <summary>
        /// 获取文件大小
        /// </summary>
        /// <param name="fileNames"></param>
        /// <returns></returns>
        public static long GetFilesSize(string[] fileNames)
        {
            return fileNames.Sum(fileName => GetFileSize(fileName));
        }

        /// <summary>
        /// 获取文件大小
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static long GetFileSize(string fileName)
        {
            if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
                return new FileInfo(fileName).Length;

            return 0;
        }

        /// <summary>
        /// 文件内容。
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static byte[] FileContent(string fileName)
        {
            FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            try
            {
                byte[] buffur = new byte[fs.Length];
                fs.Read(buffur, 0, (int)fs.Length);

                return buffur;
            }
            catch (Exception ex)
            {
                Console.WriteLine("读取文件字节流失败: {0}", ex.Message);
                return null;
            }
            finally
            {
                if (fs != null)
                {
                    //关闭资源
                    fs.Close();
                }
            }
        }

        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="content">内容</param>
        /// <param name="fileName">文件名</param>
        /// <returns></returns>
        public static bool SaveString2File(string content, string fileName)
        {
            StreamWriter sw = null;

            try
            {
                CreateDirectoyFromFileName(fileName);

                sw = new StreamWriter(fileName);
                sw.WriteLine(content);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"保存内容错误-{fileName}", ex);
                return false;
            }

            if (sw != null)
                sw.Close();

            return true;
        }

        /// <summary>
        /// 从文件加载字符串
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static string LoadStringFromFile(string fileName)
        {
            string content = string.Empty;

            StreamReader sr = null;
            try
            {
                sr = new StreamReader(fileName, System.Text.Encoding.UTF8);
                content = sr.ReadToEnd();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"读取内容错误-{fileName}", ex);
            }

            if (sr != null)
                sr.Close();

            return content;
        }

        /// <summary>
        /// 获取本地文件名
        /// </summary>
        /// <param name="lastPath"></param>
        /// <param name="displayName"></param>
        /// <param name="typeNames"></param>
        /// <returns></returns>
        public static string GetLocalFileName(string lastPath, string displayName, string[] typeNames)
        {
            OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();

            // ofd.InitialDirectory = lastPath;
            ofd.RestoreDirectory = true;

            // "动画(.flc;.fli;.gif;.swf)|*.flc;*.fli;*.gif;*.swf|Animation(.flc,.fli)|*.flc;*.fli|Gif89a(.gif)|*.gif|SWF(*.swf)|*.swf|"
            // displayName + "(*.epub|All files (*.*)|*.*";
            string filter = displayName + "(";

            filter = typeNames.Aggregate(filter, (current, ext) => current + ("." + ext + ";"));

            filter += ")|";

            for (int i = 0; i < typeNames.Length; i++)
            {
                filter += "*." + typeNames[i];
                if (i < typeNames.Length - 1)
                    filter += ";";
            }

            // filter += "|全部文件 (*.*)|*.*";
            ofd.Filter = filter;

            return ofd.ShowDialog() != DialogResult.OK ? string.Empty : ofd.FileName;
        }

        /// <summary>
        /// 获取root文件夹下所有的文件,包含子文件夹中的文件
        /// </summary>
        /// <param name="root">文件夹路径</param>
        /// <param name="extension">文件扩展名,默认为所有文件</param>
        /// <returns>root文件夹下所有的文件的列表</returns>
        public static List<string> GetAllFileList(string root, string extension = "")
        {
            if (Directory.Exists(root))
            {
                var dirList = GetAllSubDirs(root, SearchOption.AllDirectories).ToList();
                dirList.Add(root);

                var list = new List<string>();

                foreach (var dir in dirList)
                {
                    DirectoryInfo dirInfo = new DirectoryInfo(dir);
                    dirInfo.GetFiles().ToList().ForEach(f =>
                    {
                        if (!string.IsNullOrWhiteSpace(extension))
                        {
                            if (f.Extension == (extension.Contains(".") ? extension : "." + extension))
                            {
                                list.Add(f.FullName);
                            }
                        }
                        else
                        {
                            list.Add(f.FullName);
                        }
                    });
                }

                return list;
            }
            else
            {
                return null;
            }
        }

        public class InlineComparer<T> : IEqualityComparer<T>
        {
            private readonly Func<T, T, bool> getEquals;
            private readonly Func<T, int> getHashCode;

            public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
            {
                this.getEquals = equals;
                this.getHashCode = hashCode;
            }

            public bool Equals(T x, T y)
            {
                return this.getEquals(x, y);
            }

            public int GetHashCode(T obj)
            {
                return this.getHashCode(obj);
            }
        }

        /// <summary>
        /// 比较两张图片是否完全一样
        ///
        /// 速度慢,但匹配精确
        ///
        /// (方法:匹配两张图片的每一个像素)
        /// </summary>
        private bool ImageCompareArray(Bitmap firstImage, Bitmap secondImage)
        {
            bool flag = true;
            string firstPixel;
            string secondPixel;
            if (firstImage.Width == secondImage.Width && firstImage.Height == secondImage.Height)
            {
                for (int i = 0; i < firstImage.Width; i++)
                {
                    for (int j = 0; j < firstImage.Height; j++)
                    {
                        firstPixel = firstImage.GetPixel(i, j).ToString();
                        secondPixel = secondImage.GetPixel(i, j).ToString();
                        if (firstPixel != secondPixel)
                        {
                            flag = false; break;
                        }
                    }
                }
                if (flag == false)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 比较两张图片是否完全一样
        ///
        /// 速度快,但对大图片可能有些误差
        ///
        /// (方法:将图片转换为Base64后,匹配Base64)
        /// </summary>
        public static bool ImageCompareString(Bitmap firstImage, Bitmap secondImage)
        {
            MemoryStream ms = new MemoryStream();
            firstImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
            String firstBitmap = Convert.ToBase64String(ms.ToArray());
            ms.Position = 0;
            secondImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
            String secondBitmap = Convert.ToBase64String(ms.ToArray());
            if (firstBitmap.Equals(secondBitmap))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}